package com.czy.utils;

import com.czy.entity.CodeParams;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * 基于Zxing的二维码 条形码工具类
 *
 * @author czy
 * @date 2022-1-26 17:43
 */
public class CodeGenerateUtil {
    public static void main(String[] args) throws Exception {
        // 1. 生成二维码
//        String qrCodeText = "http://www.baidu.com";
        String qrCodeText = "adf123kldf—1";
        CodeParams qrCodeParams = new CodeParams(300, 300);
//        qrCodeParams.setLogoImage("D:\\\\file\\\\bb.jpg"); // 设置二维码logo
//        qrCodeParams.setOnColor(Color.CYAN.getRGB()); // 设置前景色
//        qrCodeParams.setOffColor(Color.GRAY.getRGB()); // 设置背景色
        qrCodeParams.setMargin(1); // 设置边距，既二维码和背景之间的边距
        FileOutputStream qrOut = new FileOutputStream("d://file//二维码.jpg");
        CodeGenerateUtil.generateQRCode(qrCodeText, qrCodeParams, qrOut);

        // 2.生成条形码
        String barCodeText = "123abc+-. $";
        CodeParams barCodeParams = new CodeParams(500, 100);
        barCodeParams.setBarCodeWords(barCodeText);// 设置条形码下方文字
        FileOutputStream brOut = new FileOutputStream("d://file//条形码.jpg");
        CodeGenerateUtil.generateBarCode(barCodeText, barCodeParams, brOut);

    }

    /**
     * 生成二维码 到流
     *
     * @param text         二维码内容
     * @param codeParams   二维码宽度
     * @param outputStream 输出流
     * @throws WriterException
     * @throws IOException
     */
    public static void generateQRCode(String text, CodeParams codeParams, OutputStream outputStream) throws IOException {
        BufferedImage bufferedImage = generate(text, BarcodeFormat.QR_CODE, codeParams);
        // BufferedImage 转 OutputStream
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);
        encoder.encode(bufferedImage);
    }

    /**
     * 生成二维码 到文件
     *
     * @param text       二维码内容
     * @param codeParams 二维码宽度
     * @param targetFile 目标文件
     * @throws IOException
     */
    public static void generateQRCode(String text, CodeParams codeParams, File targetFile) throws IOException {
        BufferedImage bufferedImage = generate(text, BarcodeFormat.QR_CODE, codeParams);
        // 写入文件
        ImageIO.write(bufferedImage, codeParams.getCodeFormat(), targetFile);
    }

    /**
     * 生成条形码 到流
     *
     * @param text         条形码内容
     * @param codeParams   二维码宽度
     * @param outputStream 输出流
     * @throws WriterException
     * @throws IOException
     */
    public static void generateBarCode(String text, CodeParams codeParams, OutputStream outputStream) throws IOException {
        BufferedImage bufferedImage = generate(text, BarcodeFormat.CODE_128, codeParams);
        // BufferedImage 转 OutputStream
        ImageIO.write(bufferedImage,codeParams.getCodeFormat(),outputStream);
    }

    /**
     * 生成条形码 到文件
     *
     * @param text       条形码内容
     * @param codeParams 条形码配置
     * @param targetFile 目标文件
     * @throws IOException
     */
    public static void generateBarCode(String text, CodeParams codeParams, File targetFile) throws IOException {
        BufferedImage bufferedImage = generate(text, BarcodeFormat.CODE_128, codeParams);
        // 写入文件
        ImageIO.write(bufferedImage, codeParams.getCodeFormat(), targetFile);
    }

    /**
     * 生成条形码或则二维码
     *
     * @param content
     * @param format
     * @param codeParams
     * @return
     */
    public static BufferedImage generate(String content, BarcodeFormat format, CodeParams codeParams) {
        if (codeParams == null) {
            codeParams = new CodeParams();
        }
        BitMatrix bitMatrix = buildBitMatrix(content, format, codeParams);
        BufferedImage image = toImage(bitMatrix, codeParams.getOnColor(), codeParams.getOffColor());
        Image logoImg = codeParams.getLogoImage();
        // 二维码添加logo图片
        if (null != logoImg && BarcodeFormat.QR_CODE == format) {
            // 只有二维码可以贴图
            int qrWidth = image.getWidth();
            int qrHeight = image.getHeight();
            int width;
            int height;
            // 按照最短的边做比例缩放
            if (qrWidth < qrHeight) {
                width = qrWidth / codeParams.getRatio();
                height = logoImg.getHeight(null) * width / logoImg.getWidth(null);
            } else {
                height = qrHeight / codeParams.getRatio();
                width = logoImg.getWidth(null) * height / logoImg.getHeight(null);
            }
            // 构建绘图对象
            Graphics2D graphics = image.createGraphics();
            graphics.drawImage(logoImg, qrWidth * 2 / 5, qrHeight * 2 / 5, width, height, null);
            graphics.dispose();
            logoImg.flush();
            image.flush();
        }
        // 条形码下面插入文字
        String barCodeWords = codeParams.getBarCodeWords();
        if (StringUtils.isNotBlank(barCodeWords) &&
                (BarcodeFormat.CODE_128 == format || BarcodeFormat.CODE_93 == format || BarcodeFormat.CODE_39 == format)) {
            image = insertWords(image, codeParams.getBarCodeWords());
        }

        return image;
    }

    /**
     * 条形码下面加文字
     * 复杂文字插入(https://blog.csdn.net/qq_40230848/article/details/121520931)
     * @param image 条形码图片
     * @param words 文字
     * @return 返回BufferedImage
     */
    public static BufferedImage insertWords(BufferedImage image, String words) {
        // 新的图片，把条形码下面加上文字
        if (StringUtils.isNotEmpty(words)) {
            BufferedImage outImage = new BufferedImage(image.getWidth(), image.getHeight() + 30, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = outImage.createGraphics();

            g2d.setColor(Color.WHITE);
            //填充整个屏幕
            g2d.fillRect(0, 0, outImage.getWidth(), outImage.getHeight());

            // 画条形码到新的面板
            g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
            // 画文字到新的面板
            Color color = new Color(0, 0, 0);
            g2d.setColor(Color.BLACK);
            // 字体、字型、字号
            g2d.setFont(new Font("微软雅黑", Font.PLAIN, 12));
            //文字长度
            String str = words.replace("", "  ").trim();
            int strWidth = g2d.getFontMetrics().stringWidth(str);
            //总长度减去文字长度的一半  （居中显示）
            int wordStartX = (image.getWidth() - strWidth) / 2;
            int wordStartY = image.getHeight() + 10;

            //画文字
            g2d.drawString(str, wordStartX, wordStartY + 5);

            g2d.dispose();
            outImage.flush();
            return outImage;
        }
        return image;
    }


    /**
     * BitMatrix转BufferedImage
     *
     * @param bitMatrix BitMatrix
     * @param foreColor 前景色
     * @param backColor 背景色(null表示透明背景)
     * @return
     */
    private static BufferedImage toImage(BitMatrix bitMatrix, int foreColor, Integer backColor) {
        final int width = bitMatrix.getWidth();
        final int height = bitMatrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, null == backColor ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if (bitMatrix.get(x, y)) {
                    image.setRGB(x, y, foreColor);
                } else if (null != backColor) {
                    image.setRGB(x, y, backColor);
                }
            }
        }
        return image;
    }

    /**
     * 生成BitMatrix
     *
     * @param text
     * @param format
     * @param codeParams
     * @return
     */
    private static BitMatrix buildBitMatrix(String text, BarcodeFormat format, CodeParams codeParams) {
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        BitMatrix bitMatrix = null;
        try {
            bitMatrix = multiFormatWriter.encode(text, format, codeParams.getWidth(), codeParams.getHeight(), codeParams.toHints());
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return bitMatrix;
    }

    /**
     * 去掉二维码白色边框
     *
     * @param matrix
     * @return
     */
    private static BitMatrix deleteWhite(BitMatrix matrix) {
        int[] rec = matrix.getEnclosingRectangle();
        int resWidth = rec[2] + 1;
        int resHeight = rec[3] + 1;


        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        resMatrix.clear();
        for (int i = 0; i < resWidth; i++) {
            for (int j = 0; j < resHeight; j++) {
                if (matrix.get(i + rec[0], j + rec[1])) {
                    resMatrix.set(i, j);
                }
            }
        }
        return resMatrix;
    }
}
